
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

namespace Reservations
{
	public class RoomPricingBizRules : Syrinx.BaseBizRules
	{
		public void setupProductPriceBasedOnReservation(IApplication app, IRuleContext context, IApplication app2, IRuleContext context2, IBizObj orderItem)
		{
			if (orderItem != null)
			{
				if (Cmn.IsEmpty(orderItem.get("product.partId")))
				{
					context.put("id", orderItem.get("product.id"));
					//TODO: put this dar call into an ecom bizrule and call that rather than use its dar from here!
					IBizObj productInfo = app.appData.get("DA.eCom.GetProductPartIdFromId", context) as IBizObj;
					if(productInfo != null)
						orderItem.put("product", productInfo);
				}
				if (orderItem.get("product.partId").ToString().StartsWith("RT-"))
				{
					Swaf.BGuid.Guid orderId = orderItem.get("orderId") as Swaf.BGuid.Guid;
					IBizObj salesOrder = context.RuleGroup.execute(context, "getSalesOrderDetails", orderId).CallResults as IBizObj;
					if (salesOrder != null)
					{
						DateTime arrivalDate = (DateTime)salesOrder.get("reservation.arrivalDate");
						DateTime departureDate = (DateTime)salesOrder.get("reservation.departureDate");
						TimeSpan span = departureDate - arrivalDate;
						int numDaysStay = span.Days;
						int numNightsAtNormalRate = 0;
						decimal normalNightsTotal = 0M;

						decimal regularPrice = Convert.ToDecimal(orderItem.get("product.sellPrice"));
						Dictionary<decimal, int> dailyPrices = new Dictionary<decimal, int>();

						context.put("startDate", arrivalDate);
						context.put("endDate", departureDate - new TimeSpan(1, 0, 0, 0, 0));
						context.put("companyId", ActiveCompanyId);
						context.put("partId", orderItem.get("product.partId"));
						bool hasSeasonalPrices = false;
						IBizObjList seasonalPrices = app.appData.get("DA.Reservations.GetSeasonalPricesForRoomType", context) as IBizObjList;
						if (seasonalPrices != null && seasonalPrices.numElements > 0)
						{
							hasSeasonalPrices = true;
							for (int d = 0; d < numDaysStay; d++)
							{
								bool dayUsingSeasonalPrice = false;
								DateTime day = arrivalDate.Date + new TimeSpan(d, 0, 0, 0, 0);
								foreach (IBizObj seasonRate in seasonalPrices)
									if (day >= (DateTime)seasonRate.get("startDate") && day <= (DateTime)seasonRate.get("endDate"))
									{
										int numDays = 0;
										dailyPrices.TryGetValue((decimal)seasonRate.get("roomTypePricings.[0].sellPrice"), out numDays);
										dailyPrices[(decimal)seasonRate.get("roomTypePricings.[0].sellPrice")] = ++numDays;
										dayUsingSeasonalPrice = true;
										break;
									}
								if (!dayUsingSeasonalPrice)
								{
									int numDays = 0;
									dailyPrices.TryGetValue(regularPrice, out numDays);
									dailyPrices[regularPrice] = ++numDays;
								}
							}
						}
						else
							dailyPrices[regularPrice] = numDaysStay;


						int quantity = Convert.ToInt32(orderItem.get("quantity"));
						decimal orderItemPrice = 0.0M;
						foreach (KeyValuePair<decimal, int> rate in dailyPrices)
							orderItemPrice += rate.Key * rate.Value * quantity;
						context2.put("priceIsSet", true);
						orderItem.put("price", orderItemPrice);

						//TODO: Make this actually use an article rather than a hard coded string like this for
						//the order item description.
						string itemDesc = null;
						if (!hasSeasonalPrices)
						{
							numNightsAtNormalRate = numDaysStay;
							normalNightsTotal = orderItemPrice;
							itemDesc = string.Format("{0} room(s) for {1} days stay", quantity, numDaysStay);
						}
						else
						{
							StringBuilder buff = new StringBuilder();
							buff.Append("<table>");
							foreach (KeyValuePair<decimal, int> rate in dailyPrices)
							{
								if (rate.Key == regularPrice)
								{
									numNightsAtNormalRate += rate.Value;
									normalNightsTotal = ((Decimal)rate.Key * rate.Value * quantity);
								}
								buff.AppendFormat("<tr><td>{0} days at {1} rate of</td><td align='right'>&nbsp;{2} is&nbsp;</td><td align='right'>{3}</td></tr>",
									rate.Value, rate.Key == regularPrice ? "non-peak" : rate.Key > regularPrice ? "peak" : "low season", rate.Key.ToString("N"), ((Decimal)rate.Key * rate.Value * quantity).ToString("N"));
							}
							buff.Append("</table>");
							itemDesc = buff.ToString();
						}
						orderItem.put("description", itemDesc);
						context.put("id", salesOrder.get("reservation.id"));
						context.put("numNightsAtNormalRate", numNightsAtNormalRate);
						context.put("normalNightsTotal", normalNightsTotal);
						app.appData.put("DA.Reservations.UpdateReservationNumNightsNormalRate", context);
					}
				}
			}
		}

		public void recalculateOrderItemPrices(IApplication app, IRuleContext context, IBizObj salesOrder)
		{
			IBizObjList salesOrderItems = context.RuleGroup.execute(context, "getSalesOrderItems", salesOrder.get("id")).CallResults as IBizObjList;
			if (salesOrderItems != null)
			{
				foreach (IBizObj orderItem in salesOrderItems)
					if (orderItem.get("product.partId").ToString().StartsWith("RT-"))
						context.RuleGroup.execute(context, "updateSalesOrderItem", orderItem);

				salesOrder.isNew = false;
				salesOrder.put("items", salesOrderItems);
				context.RuleGroup.execute(context, "updateSalesOrder", salesOrder);
			}
		}

		public IBizObjList getCompanyRoomTypeSeasonalRates(IApplication app, IRuleContext context, Swaf.BGuid.Guid companyId)
		{
			context.put("companyId", companyId==null?ActiveCompanyId:companyId);
			IBizObjList rates = app.appData.get("DA.Reservations.GetAllSeasonalPrices", context) as IBizObjList;
			return rates;
		}

		public void updateCompanyRoomTypeSeasonRate(IApplication app, IRuleContext context, IBizObj seasonalRate)
		{
			IBizObjList pricing = seasonalRate.get("roomTypePricings") as IBizObjList;
			context.put("companyId", ActiveCompanyId);
			context.put("season", seasonalRate);
			if (seasonalRate.get("id") == null)
				seasonalRate.put("id", new Swaf.BGuid.Guid());
			foreach (IBizObj price in pricing)
			{
				if (price.isDirty() || seasonalRate.isDirty())
				{
					context.put("price", price);
					app.appData.put("DA.Reservations.DeleteSeasonRoomRate", context);
					if (((decimal)price.get("sellPrice")).CompareTo(0.0M) != 0)
						app.appData.put("DA.Reservations.AddSeasonRoomRate", context);
				}
			}
		}

		public void deleteCompanyRoomTypeSeasonRate(IApplication app, IRuleContext context, Swaf.BGuid.Guid seasonId)
		{
			context.put("id", seasonId);
			app.appData.put("DA.Reservations.DeleteRateSeason", context);
		}
	}
}
